package org.gdstash.db.criteria;

import org.gdstash.db.GDDBData;
import org.gdstash.db.SelectionCriteria;
import org.gdstash.util.GDMsgFormatter;
import org.gdstash.util.GDMsgLogger;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.LinkedList;
import java.util.List;

public class ItemIDItemSetCombination extends AbstractItemCombination {
    public ItemIDItemSetCombination() {
    }

    public ItemIDItemSetCombination(final ItemIDItemSetCombination combo) {
        super(combo);
    }

    public ItemIDItemSetCombination(final SelectionCriteria criteria) {
        super(criteria);
    }

    @Override
    public ItemIDItemSetCombination clone() {
        final ItemIDItemSetCombination combo = new ItemIDItemSetCombination(this);
        return combo;
    }

    @Override
    public String determineLevel1Statement(final CriteriaCombination.Soulbound soulbound, final CriteriaCombination.SC_HC schc, final boolean isHardcore, final String charName) {
        String command = "SELECT I.ITEM_ID FROM GD_ITEM I";
        final boolean statFilled = !this.getCriteria().statInfos.isEmpty();
        if (this.getCriteria().isSkillBonusUsed()) {
            command = command + ", " + "GD_ITEMSET_SKILLS" + " ISB, " + "GD_ITEMSET_SKILL_LEVEL" + " ISBL";
        }
        if (this.getCriteria().isSkillModifierUsed()) {
            command = command + ", " + "GD_ITEMSET_SKILLMODIFIER" + " ISM";
        }
        if (statFilled) {
            command = command + ", " + "GD_ITEMSET_STAT" + " IST";
        }
        command += " WHERE ";
        if (this.getCriteria().isSkillBonusUsed()) {
            command += "I.SET_ID = ISB.ITEMSET_ID AND I.SET_ID = ISBL.ITEMSET_ID AND ISB.TYPE = ISBL.TYPE AND ISB.INDEX = ISBL.INDEX AND ";
        }
        if (this.getCriteria().isSkillModifierUsed()) {
            command += "I.SET_ID = ISM.ITEMSET_ID AND ";
        }
        if (statFilled) {
            command += "I.SET_ID = IST.ITEMSET_ID AND ";
        }
        command += "I.ITEM_CLASS = ?";
        if (this.usesItemRarity()) {
            command += " AND I.RARITY = ?";
        }
        if (this.usesArmorClass()) {
            command += " AND I.ARMOR_CLASS = ?";
        }
        if (this.usesArtifactClass()) {
            command += " AND I.ARTIFACT_CLASS = ?";
        }
        if (this.usesItemName()) {
            command += " AND UPPER(I.NAME_FULL) LIKE ?";
        }
        if (this.usesSetItem()) {
            command += " AND I.SET_ID IS NOT NULL";
        }
        if (this.usesBonusSkill()) {
            command += " AND ISB.BONUS_ENTITY = ? AND ISBL.BONUS_VALUE > 0";
        }
        if (this.usesMasteryBonusSkills()) {
            command += " AND ISB.MASTERY_ID = ? AND ISBL.BONUS_VALUE > 0";
        }
        if (this.usesAllSkills()) {
            command += " AND I.PLUS_ALLSKILLS > 0";
        }
        if (this.usesSkillModifier()) {
            command += " AND ISM.SKILL_ID = ?";
        }
        if (this.usesMasteryModifySkills()) {
            command += " AND ISM.MASTERY_ID = ?";
        }
        if (this.usesAllItemSkills()) {
            command += " AND I.ITEM_SKILL_ID IS NOT NULL";
        }
        if (this.usesItemSkill()) {
            command += " AND I.ITEM_SKILL_ID = ?";
        }
        if (this.getMinLevel() != -1) {
            command += " AND I.REQ_LEVEL >= ?";
        }
        if (this.getMaxLevel() != -1) {
            command += " AND I.REQ_LEVEL <= ?";
        }
        if (this.getMaxCunning() != -1) {
            command += " AND I.REQ_DEX <= ?";
        }
        if (this.getMaxPhysique() != -1) {
            command += " AND I.REQ_STR <= ?";
        }
        if (this.getMaxSpirit() != -1) {
            command += " AND I.REQ_INT <= ?";
        }
        if (this.usesPetBonus()) {
            command += " AND I.PET_BONUS_SKILL_ID IS NOT NULL";
        }
        if (this.usesConversionFrom()) {
            if (this.usesConversionTo()) {
                command += " AND (( I.CONVERT_IN = ? AND I.CONVERT_OUT = ? ) OR ( I.CONVERT_IN_2 = ? AND I.CONVERT_OUT_2 = ? ))";
            } else {
                command += " AND (( I.CONVERT_IN = ? ) OR ( I.CONVERT_IN_2 = ? ))";
            }
        } else if (this.usesConversionTo()) {
            command += " AND (( I.CONVERT_OUT = ? ) OR ( I.CONVERT_OUT_2 = ? ))";
        }
        if (this.isNoEnemyOnly()) {
            command += " AND I.ENEMY_ONLY = ?";
        }
        command += this.determineSlotConditions("I.");
        return command;
    }

    @Override
    public void addSingleStringCombo(final List<String> listAll, final PreparedStatement ps, final String command, final String statType) throws SQLException, UnsupportedOperationException {
        int nextPos = this.fillLevel1Statement(ps);
        if (statType != null) {
            nextPos = this.fillLevel2Statement(ps, statType, nextPos);
        }
        try (final ResultSet rs = ps.executeQuery()) {
            final List<String> ids = AbstractItemCombination.wrapString(rs, 1);
            if (ids != null) {
                AbstractItemCombination.mergeStringList(listAll, ids);
            }
        } catch (SQLException ex) {
            final Object[] args = {command, this.determineLevel1Parameters()};
            final String msg = GDMsgFormatter.format(GDMsgFormatter.rbMsg, "ERR_SELECT_FAILED", args);
            GDMsgLogger.addError(msg);
            throw ex;
        }
    }

    public static List<String> getItemIDs(final SelectionCriteria criteria, final CriteriaCombination.Soulbound soulbound, final CriteriaCombination.SC_HC schc, final boolean isHardcore, final String charName) {
        final ItemIDItemSetCombination combo = new ItemIDItemSetCombination(criteria);
        return getItemIDs(combo, soulbound, schc, isHardcore, charName);
    }

    public static List<String> getItemIDs(final ItemIDItemSetCombination combo, final CriteriaCombination.Soulbound soulbound, final CriteriaCombination.SC_HC schc, final boolean isHardcore, final String charName) {
        final List<String> listAll = new LinkedList<String>();
        try (final Connection conn = GDDBData.getConnection()) {
            final List<CriteriaCombination> combos = combo.createLevel1Combinations(combo);
            for (final CriteriaCombination cc : combos) {
                final String command1 = cc.determineLevel1Statement(soulbound, schc, isHardcore, charName);
                if (cc.getCriteria().statInfos.isEmpty()) {
                    try (final PreparedStatement ps = conn.prepareStatement(command1)) {
                        try {
                            cc.addSingleStringCombo(listAll, ps, command1, null);
                        } catch (SQLException ex) {
                            throw ex;
                        }
                    } catch (SQLException ex3) {
                        final Object[] args = {command1};
                        final String msg = GDMsgFormatter.format(GDMsgFormatter.rbMsg, "ERR_PREPARE_FAILED", args);
                        GDMsgLogger.addError(msg);
                    }
                    conn.commit();
                } else {
                    for (final SelectionCriteria.StatInfo info : cc.getCriteria().statInfos) {
                        final String command2 = cc.determineLevel2Statement(command1, info);
                        for (final String statType : info.statTypes) {
                            try (final PreparedStatement ps2 = conn.prepareStatement(command2)) {
                                try {
                                    cc.addSingleStringCombo(listAll, ps2, command2, statType);
                                } catch (SQLException ex2) {
                                    throw ex2;
                                }
                            } catch (SQLException ex4) {
                                final Object[] args2 = {command2};
                                final String msg2 = GDMsgFormatter.format(GDMsgFormatter.rbMsg, "ERR_PREPARE_FAILED", args2);
                                GDMsgLogger.addError(msg2);
                            }
                            conn.commit();
                        }
                    }
                }
            }
        } catch (SQLException ex5) {
            GDMsgLogger.addError(GDMsgFormatter.getString(GDMsgFormatter.rbMsg, "ERR_DB_CONN_FAILED"));
        }
        return listAll;
    }
}
